package vip.liux.backend.application.serviceImpl.shop;

import com.google.common.collect.ImmutableMap;
import jakarta.annotation.Nullable;
import org.apache.commons.collections4.MapUtils;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.stereotype.Service;
import vip.liux.backend.application.service.shop.RegionDto;
import vip.liux.backend.application.service.shop.RegionService;
import vip.liux.backend.domain.models.shop.address.RegionRepository;
import vip.liux.backend.domain.shared.shop.AreaLevel;

import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

@Service
public class RegionServiceImpl implements RegionService {

    private final RegionRepository regionRepository;

    private final Cache cache;

    public static final String VERSION = "1.0.0";


    public RegionServiceImpl(RegionRepository regionRepository, CacheManager cacheManager) {
        this.regionRepository = regionRepository;
        this.cache = cacheManager.getCache("region");
    }

    @Override
    public String getNameById(@Nullable Long id) {
        Map<Long, RegionDto> data = getCacheAll();
        if (id == null) {
            return "其他";
        }

        return data.containsKey(id) ? data.get(id).getName() : "其他";
    }

    @Override
    public Long getIdByName(String name, AreaLevel level, @Nullable Long parentId) {
        Map<Long, RegionDto> all = getCacheAll();
        for (Map.Entry<Long, RegionDto> entry : all.entrySet()) {
            RegionDto item = entry.getValue();
            if (item.getName().equals(name) && item.getLevel() == level && Objects.equals(item.getParentId(), parentId)) {
                return item.getId();
            }
        }
        return null;
    }

    @SuppressWarnings("unchecked")
    @Override
    public Map<Long, RegionDto> getCacheAll() {
        return (Map<Long, RegionDto>) regionCache().get("all");
    }

    @SuppressWarnings("unchecked")
    @Override
    public Map<Long, RegionDto> getCacheTree() {
        return (Map<Long, RegionDto>) regionCache().get("tree");
    }

    @SuppressWarnings("unchecked")
    @Override
    public Map<String, Long> getCacheCounts() {
        return (Map<String, Long>) regionCache().get("count");
    }

    @SuppressWarnings("unchecked")
    private Map<String, Object> regionCache() {
        Map<String, Object> complete = (Map<String, Object>) cache.get("region", Map.class);

        if (!MapUtils.isEmpty(complete) && VERSION.equals(complete.get("version"))) {
            return complete;
        }

        Map<Long, RegionDto> allList = getAllList();

        complete = ImmutableMap.<String, Object>builder()
                .put("all", allList)
                .put("tree", getTreeList(new HashMap<>(allList)))
                .put("count", getCount(allList))
                .put("version", VERSION)
                .build();
        cache.putIfAbsent("region", complete);
        return complete;
    }


    private Map<String, Long> getCount(Map<Long, RegionDto> allList) {
        Collection<RegionDto> regions = allList.values();
        return ImmutableMap.<String, Long>builder()
                .put("total", (long) regions.size())
                .put("province", regions.stream().filter(region -> region.getLevel() == AreaLevel.province).count())
                .put("city", regions.stream().filter(region -> region.getLevel() == AreaLevel.city).count())
                .put("district", regions.stream().filter(region -> region.getLevel() == AreaLevel.district).count())
                .build();
    }

    private Map<Long, RegionDto> getTreeList(Map<Long, RegionDto> allList) {
        Map<Long, RegionDto> treeList = new HashMap<>();
        for (Map.Entry<Long, RegionDto> provinceEntry : allList.entrySet()) {
            RegionDto province = provinceEntry.getValue();
            if (province.getLevel() == AreaLevel.province) { // 省份
                treeList.put(provinceEntry.getKey(), province);
                for (Map.Entry<Long, RegionDto> cityEntry : allList.entrySet()) {
                    RegionDto city = cityEntry.getValue();
                    if (city.getLevel() == AreaLevel.city && Objects.equals(province.getId(), city.getParentId())) { // 城市
                        treeList.get(province.getId()).getCity().put(cityEntry.getKey(), city);
                        for (Map.Entry<Long, RegionDto> districtEntry : allList.entrySet()) {
                            RegionDto district = districtEntry.getValue();
                            if (district.getLevel() == AreaLevel.district && Objects.equals(city.getId(), district.getParentId())) { // 区县
                                treeList.get(province.getId()).getCity().get(city.getId()).getRegion().put(districtEntry.getKey(), district);
                            }
                        }
                    }
                }
            }
        }

        return treeList;
    }

    private Map<Long, RegionDto> getAllList() {
        return regionRepository.findAll().stream()
                .map(region -> new RegionDto(region.getId(), region.getCode(), region.getName(), region.getParentId(), region.getLevel()))
                .collect(Collectors.toMap(RegionDto::getId, regionDto -> regionDto, (a, b) -> a));
    }
}
